package com.code.app.exception;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;

import com.code.app.annotation.Tip;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 单例模式 ：全局崩溃信息处理器
 */

public class ExceptionHandler implements Thread.UncaughtExceptionHandler {

    private final String TAG = "ExceptionHandler";

    private static final String PATH = Environment.getExternalStorageDirectory().getAbsolutePath();

    private static final String FILE_NAME_SUFFIX = ".trace";

    private volatile static ExceptionHandler exceptionHandler;

    private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;

    private Context context;

    @Tip(msg="Double Check Lock(DCL)模式（双重检查锁定模式）")
    public static ExceptionHandler getInstance() {
        if (exceptionHandler == null) {
            //解决多并发的问题
            synchronized (ExceptionHandler.class) {
                if (exceptionHandler == null) {
                    exceptionHandler = new ExceptionHandler();
                }
            }
        }
        return exceptionHandler;
    }


    public void init(Context context) {
        this.context = context;
        this.uncaughtExceptionHandler = Thread.currentThread().getDefaultUncaughtExceptionHandler();
        //设置全局异常类为本类
        Thread.currentThread().setUncaughtExceptionHandler(this);

    }

    private ExceptionHandler() {
    }

    /**
     * 当程序中有未被捕获的异常，系统将会调用这个方法
     *
     * @param t 出现未捕获异常的线程
     * @param e 得到异常信息
     */
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        try {
            //保存到本地
            exportExceptionToSDCard(e);
            //下面也可以写上传的服务器的代码
        } catch (Exception e1) {
            e1.printStackTrace();
            Log.e(TAG,"异常信息保存失败") ;
        }
        if (uncaughtExceptionHandler != null) {   //把异常交给系统处理 必须要加上 否则不会打印logcat
            uncaughtExceptionHandler.uncaughtException(t, e);
        }
    }

    /**
     * 导出异常信息到SD卡
     *
     * @param e
     */
    private void exportExceptionToSDCard(@NonNull Throwable e) {
        //判断SD卡是否存在
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        File file = new File(PATH + File.separator + time + FILE_NAME_SUFFIX);
        try {
            //往文件中写入数据
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            pw.println(time);
            pw.println(appendPhoneInfo());
            e.printStackTrace(pw);
            pw.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (PackageManager.NameNotFoundException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 获取手机信息
     */
    private String appendPhoneInfo() throws PackageManager.NameNotFoundException {
        PackageManager pm = context.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
        StringBuilder sb = new StringBuilder();
        //App版本
        sb.append("App Version: ");
        sb.append(pi.versionName);
        sb.append("_");
        sb.append(pi.versionCode + "\n");

        //Android版本号
        sb.append("OS Version: ");
        sb.append(Build.VERSION.RELEASE);
        sb.append("_");
        sb.append(Build.VERSION.SDK_INT + "\n");

        //手机制造商
        sb.append("Vendor: ");
        sb.append(Build.MANUFACTURER + "\n");

        //手机型号
        sb.append("Model: ");
        sb.append(Build.MODEL + "\n");

        //CPU架构
        sb.append("CPU: ");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            sb.append(Arrays.toString(Build.SUPPORTED_ABIS));
        } else {
            sb.append(Build.CPU_ABI);
        }
        return sb.toString();
    }
}
